/*
 * @Author: Suo-Yuheng syh15399227325@163.com
 * @Date: 2025-09-07 17:00:51
 * @LastEditors: Suo-Yuheng syh15399227325@163.com
 * @LastEditTime: 2025-09-08 16:18:27
 * @FilePath: /0907/udp/UdpServer.hpp
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#pragma once

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <string>
#include <unordered_map>
#include <strings.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <functional>
#include "log.hpp"
using namespace std;

// using func_t = function<string(const string&)>;
typedef function<string(const string&, const string &, uint16_t)> func_t;

extern log Log;

enum
{
    SOCKET_ERR = 1,
    BIND_ERR,
};

uint16_t defaultport = 8080;
string defaultip = "0.0.0.0";
const int size = 1024;

class UdpServer
{
public:
    UdpServer(const uint16_t &port = defaultport, const string &ip = defaultip, bool isRunnig = false)
        :_sockfd(0), _port(port), _ip(ip)
    {
    }

    void Init()
    {
        // 1 创建 udp 套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            Log(Fatal, "socket create error, sockfd: %d", _sockfd);
            exit(SOCKET_ERR);
        }
        Log(Info, "socket create success, sockfd: %d", _sockfd);

        // 2 绑定 端口
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;                     // 协议家族、域、、ipv4
        local.sin_port = htons(_port);                  // 端口号、、但是需要保证是 网络字节序列 —— 大端，因为该端口号是要给对方发送的
        // local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 1、string -》 uint32_t     2、这个 ip 还必须是 网络字节序列的
        local.sin_addr.s_addr = INADDR_ANY; // 任意绑定

        //      准备好了数据，现在将他们设置进 内核的 套接字
        int n = bind(_sockfd, (const struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            Log(Fatal, "bind error, errno: %d, error string: %s", errno, strerror(errno));
            exit(BIND_ERR);
        }
        //      至此，绑定成功
        Log(Info, "bind success, errno: %d, error string: %s", errno, strerror(errno));
    }


    void CheckUser(const struct sockaddr_in &client, const string &clientip, const uint16_t &clientport)
    {
        auto it = _online_user.find(clientip);
        if(it == _online_user.end())
        {
            _online_user.insert({clientip, client});
            cout << "[" << clientip << ":" << clientport << "] add to online user!" << endl;
        }
    }


    void Broadcast(const string &info, const string clientip, uint16_t clientport)
    {
        for (const auto &user : _online_user)
        {
            string message = "[";
            message += clientip;
            message += ":";
            message += to_string(clientport);
            message += "]# ";
            message += info;
            socklen_t len = sizeof(user.second);
            sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)(&user.second), len);
        }
    }

    // void Run(func_t func)
    void Run()
    {
        // 作为一款服务器，需要一直运行
        _isRunnig = true;
        char inbuffer[size];

        while (_isRunnig)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            ssize_t n = recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr *)&client, &len); // 接收
            if (n < 0)
            {
                Log(Warning, "recvfrom error, errno: %d, error string: %s", errno, strerror(errno));
                continue;
            }

            uint16_t clientport = ntohs(client.sin_port); // 用户的端口号
            string clientip = inet_ntoa(client.sin_addr); // 用户 ip

            CheckUser(client, clientip, clientport);

            string info = inbuffer;
            Broadcast(info, clientip, clientport);  // 广播给所有人


            // inbuffer[n] = 0;    // recvfrom 成功会返回读取到的字节数，而我们暂时把 inbuffer 中的数据 当作字符串看
            
            // // 充当一次服务器对 数据的处理
            // string info = inbuffer;
            // string echo_string = func(info, clientip, clientport);    // 包装器调用 可调用对象,,这样就是在对 代码进行  分层

            // // string echo_string = "sever echo# " + info;

            // // cout << echo_string << endl;    // 调试

            // sendto(_sockfd, echo_string.c_str(), echo_string.size(), 0, (const sockaddr *)&client, len);    // 发送
        }
    }

    ~UdpServer()
    {
        if(_sockfd > 0)
        {
            close(_sockfd);
        }
    }

private:
    int _sockfd;    // 网路文件描述符
    string _ip;     // ？？？？？  任意地址绑定
    uint16_t _port; // 端口
    bool _isRunnig;
    unordered_map<string, struct sockaddr_in> _online_user;
};